Class pgWarpPawnHandle extends Info;

var pgPortalDecal Start,End;
var vector DesiredPosition,DesiredVelocity,StartingPosition;
var byte TeleState;
var Pawn WarpPawn;
var bool bClientSecSet;

replication
{
	reliable if ( Role == ROLE_Authority && bNetInitial && bNetOwner )
		DesiredVelocity;
	reliable if ( Role == ROLE_Authority && bNetOwner )
		Start,End,DesiredPosition,TeleState,WarpPawn;
}

function PostBeginPlay()
{
	WarpPawn = Pawn(Owner);
	StartingPosition = WarpPawn.Location;
	DesiredVelocity = WarpPawn.Velocity;
	WarpPawn.SetCollision(false,false,false);
	WarpPawn.bCollideWorld = false;
	WarpPawn.SetPhysics(PHYS_None);
	WarpPawn.Disable('Landed');
}
final function SetPortal( pgPortalDecal P )
{
	Start = P;
	End = P.OtherSide;
	P.PlaySound(Sound'Portal_EntryG');
}
function Destroyed()
{
	if( WarpPawn!=None && WarpPawn.Health>0 )
	{
		WarpPawn.SetCollision(true,true,true);
		WarpPawn.bCollideWorld = true;
		WarpPawn.SetPhysics(PHYS_Falling);
		WarpPawn.Velocity = DesiredVelocity;
		WarpPawn.Enable('Landed');
		if( End!=None )
			End.PlaySound(Sound'Portal_ExitG');
	}
}
simulated final function UpdateViewRot()
{
	local rotator R;

	R = WarpPawn.GetViewRotation();
	Start.WarpToCoordsRot(R);
	WarpPawn.SetViewRotation(R);
}
simulated final function vector GetDestWarp() // Get warp dest location on portal surface
{
	local vector V;

	V = DesiredPosition-End.Location;
	V-=(V Dot End.ForwardDir)*End.ForwardDir;
	return V+End.Location;
}
final function NotifyWarping()
{
	local Controller P;

	for (p=Level.ControllerList; P!=None; P=P.NextController )
		if (P.Enemy == WarpPawn)
			P.LineOfSightTo(WarpPawn);
	if( WarpPawn.Controller!=None )
		WarpPawn.Controller.MoveTimer = -1.0;
}
simulated function Tick( float Delta )
{
	if( WarpPawn==None || WarpPawn.Health<=0 )
	{
		if( Level.NetMode!=NM_Client )
			Destroy();
		return;
	}
	if( Start==None || End==None ) // Either one of portals was destroyed, abort teleportation
	{
		if( Level.NetMode==NM_Client ) // May be delayed replication
			return;
		if( TeleState==0 )
			WarpPawn.SetLocation(StartingPosition);
		else WarpPawn.SetLocation(DesiredPosition);
		Destroy();
		return;
	}
	switch( TeleState )
	{
	case 0:
		if( Start.ForwardDir.Z>0.8f && DesiredVelocity.Z>-300 ) // Walked into portal, make it fall!
			DesiredVelocity.Z-=950.f*Delta;

		WarpPawn.Velocity = FMin((DesiredVelocity Dot Start.ForwardDir),-50.f) * Start.ForwardDir;
		WarpPawn.Move(WarpPawn.Velocity*Delta);
		if( (((WarpPawn.Location+WarpPawn.EyeHeight*vect(0,0,1)) Dot Start.ForwardDir)-Start.PlaneDepth)<0.f || LifeSpan<2.f ) // Camera passed portal.
		{
			bClientSecSet = true;
			if( WarpPawn.IsLocallyControlled() && WarpPawn.IsHumanControlled() )
				PlayerController(WarpPawn.Controller).ClientFlash(1.f,vect(300,300,300));
			Start.WarpToCoordsAxis(DesiredVelocity);
			UpdateViewRot();
			if( Level.NetMode!=NM_Client )
				NotifyWarping();
			WarpPawn.SetLocation(GetDestWarp());
			++TeleState;
		}
		break;
	case 1:
		if( Start.ForwardDir.Z<-0.7f ) // Fall out from ceiling!
			DesiredVelocity.Z-=950.f*Delta;

		WarpPawn.Velocity = FMax((DesiredVelocity Dot End.ForwardDir),50.f) * End.ForwardDir;
		WarpPawn.Move(WarpPawn.Velocity*Delta);
		if( ((WarpPawn.Location Dot End.ForwardDir)-End.PlaneDepth-End.BoxPushOut(WarpPawn)-20.f)>0.f ) // Pawn exited the portal.
		{
			++TeleState;
			Destroy();
		}
		break;
	default:
		Destroy();
	}
}
simulated function PostNetReceive()
{
	// Used in case server sends client state 1 before client sets it.
	if( !bClientSecSet && TeleState==1 && Start!=None && End!=None )
	{
		bClientSecSet = true;
		if( WarpPawn.IsHumanControlled() )
			PlayerController(WarpPawn.Controller).ClientFlash(1.f,vect(300,300,300));
		Start.WarpToCoordsAxis(DesiredVelocity);
		UpdateViewRot();
	}
}

defaultproperties
{
	RemoteRole=ROLE_SimulatedProxy
	bOnlyRelevantToOwner=true
	LifeSpan=4
	bSkipActorPropertyReplication=true
	bNetNotify=true
}